/**
 *	@file	fld_safari.c
 *	@brief	フィールド　サファリ処理
 *	@author	Miyuki Iwasawa
 *	@date	08.10.24
 */

#include "common.h"

#include "fieldsys.h"

#include "field_subproc.h"
#include "field/field.h"
#include "field/situation_local.h"
#include "field/script.h"
#include "field/syswork.h"
#include "field/fld_bgm.h"
#include "field/sysflag.h"
#include "fieldmap.h"
#include "map_attr.h"
#include "zonedata.h"
#include "map_tool.h"
#include "system/mystatus.h"
#include "application/safari_l.h"

#include "fielddata/maptable/zone_id.h"

#include "safari/safari_sys.h"
#include "fld_safari.h"

#ifdef DEBUG_ONLY_FOR_iwasawa
 #undef IWASAWA_PRINT
 #define IWASAWA_PRINT(...) ((void) 0)
#endif


static BOOL attr_ObjPlaceEnable1x2(FIELDSYS_WORK* fsys,	s16 gx,s16 gz,u8 dir,fx32 gy,s16* px,s16* pz,u8 mode);
static BOOL attr_ObjPlaceEnable2x1(FIELDSYS_WORK* fsys,	s16 gx,s16 gz,u8 dir,fx32 gy,s16* px,s16* pz,u8 mode);
static BOOL attr_ObjPlaceEnable2x2(FIELDSYS_WORK* fsys,	s16 gx,s16 gz,u8 dir,fx32 gy,s16* px,s16* pz,u8 mode);
static BOOL attr_IsObjPlace(MATR matr,BOOL hit,SAFOBJ_PLACE_MODE mode);

/**
 *	@brief	サファリのゾーンかどうかチェック
 */
BOOL SafariField_ZoneCheck(int zoneID)
{
	switch(zoneID){
	case ZONE_ID_D47R0102:
		return TRUE;
	default:
		break;
	}
	return FALSE;
}

/**
 *	@brief	プレイヤーが現在いるブロックIDを取得
 */
int SafariField_GetBlockIdFromXZ(int gx,int gz)
{
	int id;
	//プレイヤーがいるブロックIDを取得
	id = ((gz-32)/32)*SAFARI_FIELD_X+((gx-32)/32);
	if(id < 0 || id >= SAFARI_BLOCK_MAX){
		return 0;
	}
	return id;
}

/**
 *	@brief	プレイヤーが現在いるブロックIDを取得
 */
int SafariField_GetNowBlockID(FIELDSYS_WORK* fsys)
{
	return SafariField_GetBlockIdFromXZ(
		Player_NowGPosXGet(fsys->player),
		Player_NowGPosZGet(fsys->player));
}

/**
 *	@brief	サファリのアトリビュートチェックが有効か？
 */
BOOL SafariAttr_IsEnable(FIELDSYS_WORK* fsys)
{
	EVENTWORK * ev = SaveData_GetEventWork(fsys->savedata);
	SITUATION * sit = SaveData_GetSituation(fsys->savedata);
	LOCATION_WORK * now = Situation_GetNowLocation(sit);
	SAFARI_SAVE* safari_sv = SaveData_GetSafariSave(fsys->savedata);
	//サファリゾーンマップかどうか？
	if(!SafariField_ZoneCheck(fsys->location->zone_id)){
		return FALSE;
	}
	//サファリプレイ中か？
	if(!SysFlag_SafariCheck(ev)){
		return FALSE;
	}
	//ゲームで参照するレイアウトが自分のものか？
	if(SafariSv_GetGameLayoutID(safari_sv) != SAFARI_LAYOUT_MINE){
		return FALSE;
	}

	//カスタマイズオープンフラグは立っているか？
	if(SafariSv_GetObjLevel(safari_sv) == SAFARI_OBJ_LEVEL0 ){
		return FALSE;
	}

	/*
	 * プレイヤーが有効ブロック内にいるか？
	 * プレイヤーの現在地点と一つ前の座標の高さが同一かどうか？
	 */
	{
		VecFx32 vec;
		fx32	h1,h2;
		u8 out;
		int dir = Player_DirGet(fsys->player);
		int gx = Player_NowGPosXGet(fsys->player);
		int gz = Player_NowGPosZGet(fsys->player);
		int gfx = gx += FieldOBJ_DirAddValueGX(dir);
		int gfz = gz += FieldOBJ_DirAddValueGZ(dir);

		if(gx < 32 || (gx >= 128) || gz < 32 || (gz >= 96)){
			return FALSE;
		}
		if(gfx < 32 || (gfx >= 128) || gfz < 32 || (gfz >= 96)){
			return FALSE;
		}
		Player_VecPosGet(fsys->player, &vec );

		h1 = GetGroundMapHeight( fsys, vec.y, vec.x, vec.z, &out);
		if(out != HEIGHT_NORMAL){
			return FALSE;
		}
		h2 = GetGroundMapHeight( fsys, vec.y,
				FX32_CONST(gfx*16+8),FX32_CONST(gfz*16+8), &out);
		if(out != HEIGHT_NORMAL){
			return FALSE;
		}
		if(h1 != h2){
			return FALSE;
		}
	}
	return TRUE;
}

/**
 *	@brief	サファリの地上オブジェクトをおけるアトリビュートかチェック
 *
 *	＊BOOL SafariAttr_IsEnable(FIELDSYS_WORK* fsys)がTRUEのときでないと
 *	　このチェックも無効
 */
BOOL SafariAttr_IsGroundObjPlace(MATR matr,BOOL hit)
{
	if(hit){
		return FALSE;
	}
	return MATR_IsSafariGroundObjPlace(matr);
}

/**
 *	@brief	サファリの水上オブジェクトをおけるアトリビュートかチェック
 *
 *	＊BOOL SafariAttr_IsEnable(FIELDSYS_WORK* fsys)がTRUEのときでないと
 *	　このチェックも無効
 */
BOOL SafariAttr_IsWaterObjPlace(MATR matr,BOOL hit)
{
	if(hit){
		return FALSE;
	}
	return MATR_IsSafariWaterObjPlace(matr);
}


/**
 *	@brief	サファリオブジェレイアウトツール呼び出しデータセット
 */
SAFARI_LTPARAM* FieldSafariObjLayout_CallDataSet(FIELDSYS_WORK* fsys,int heapID)
{
	SAFARI_LTPARAM * sp = NULL;
	s16 gx,gz,px,pz;
	u8	dir,mode,out;
	VecFx32	vec;

	sp = sys_AllocMemoryLo(heapID, sizeof(SAFARI_LTPARAM));
	MI_CpuClear8(sp,sizeof(SAFARI_LTPARAM));

	sp->save		= GameSystem_GetSaveData(fsys);
	sp->kt_status	= &fsys->KeyTouchStatus;
	sp->cursor_p	= &fsys->SafariObjCursorPos;

	//自機の状態からチェックするモードを取得する
	if( Player_FormGet(fsys->player) != HERO_FORM_SWIM ){
		mode = SAFOBJ_PLACE_MODE_GROUND;
	}else{
		mode = SAFOBJ_PLACE_MODE_WATER;
	}
	sp->place_mode = mode;

	//プレイヤーの現在グリッド座標取得
	dir = Player_DirGet(fsys->player);
	gx = Player_NowGPosXGet(fsys->player);
	gz = Player_NowGPosZGet(fsys->player);
	Player_VecPosGet(fsys->player, &vec );

	//プレイヤーがいるブロックIDを取得
	sp->block_id = SafariField_GetBlockIdFromXZ(gx,gz);

	//現在地点の高さを取得
	vec.y = GetGroundMapHeight( fsys, vec.y, vec.x, vec.z, &out);
	if(out != HEIGHT_NORMAL){
		//高さが取れなかった場合、何も置けないことにする
		return sp;
	}
	sp->height = (u8)FX_Whole(vec.y);

	//ここに来た以上、1x1は必ずおけるハズ
	sp->obj_spc[SAFOBJ_SIZ_1x1].x = gx+FieldOBJ_DirAddValueGX(dir);
	sp->obj_spc[SAFOBJ_SIZ_1x1].z = gz+FieldOBJ_DirAddValueGZ(dir);
	sp->obj_spc[SAFOBJ_SIZ_1x1].valid = TRUE;
	IWASAWA_PRINT(" SafariObjCheck 1x1 OK %d : %d \n",
		sp->obj_spc[SAFOBJ_SIZ_1x1].x,
		sp->obj_spc[SAFOBJ_SIZ_1x1].z);

	//1x2をチェック
	if(attr_ObjPlaceEnable1x2( fsys, gx,gz,dir,vec.y,&px,&pz,mode)){
		sp->obj_spc[SAFOBJ_SIZ_1x2].x = px;
		sp->obj_spc[SAFOBJ_SIZ_1x2].z = pz;
		sp->obj_spc[SAFOBJ_SIZ_1x2].valid = TRUE;
		IWASAWA_PRINT(" SafariObjCheck 1x2 OK %d : %d \n",px,pz);
	}
	//2x1をチェック
	if(attr_ObjPlaceEnable2x1( fsys, gx,gz,dir,vec.y,&px,&pz,mode)){
		sp->obj_spc[SAFOBJ_SIZ_2x1].x = px;
		sp->obj_spc[SAFOBJ_SIZ_2x1].z = pz;
		sp->obj_spc[SAFOBJ_SIZ_2x1].valid = TRUE;
		IWASAWA_PRINT(" SafariObjCheck 2x1 OK %d : %d \n",px,pz);
	}
	//2x2をチェック
	if(attr_ObjPlaceEnable2x2( fsys, gx,gz,dir,vec.y,&px,&pz,mode)){
		sp->obj_spc[SAFOBJ_SIZ_2x2].x = px;
		sp->obj_spc[SAFOBJ_SIZ_2x2].z = pz;
		sp->obj_spc[SAFOBJ_SIZ_2x2].valid = TRUE;
		IWASAWA_PRINT(" SafariObjCheck 2x2 OK %d : %d \n",px,pz);
	}
	return sp;
}

/**
 *	@brief	噴水を所持しているかどうか？
 */
static BOOL safari_WaterObjCheck(SAVEDATA* save)
{
	u8 add_id;
	u32 my_id = MyStatus_GetID(SaveData_GetMyStatus(save));
	u8	obj_level = SafariSv_GetObjLevel(SaveData_GetSafariSave(save));

	//OBJの追加タイミングをチェックしている

	add_id = my_id%10;	//下一桁をチェック
	if(add_id < 6){
		add_id = (add_id/3);
	}else{
		add_id = ((add_id-6)/2)+2;
	}
	add_id = 3-add_id;
	if(obj_level >= (add_id+1)){
		return TRUE;	//追加されている
	}
	return FALSE;	//まだ
}

/*
 *	@brief	サファリのオブジェクトを置けるかどうかスペースなどのチェック
 *
 *	0が返れば、問題なくOBJを置け
 */
int FieldSafari_ObjPlaceCheck(FIELDSYS_WORK* fsys)
{
	s16 gx,gz,px,pz;
	u8	dir,block_id;
	VecFx32	vec;
	SAFARI_LAYOUT* layout;

	dir = Player_DirGet(fsys->player);
	gx = Player_NowGPosXGet(fsys->player);
	gz = Player_NowGPosZGet(fsys->player);
	Player_VecPosGet(fsys->player, &vec );

	block_id = ((gz-32)/32)*SAFARI_FIELD_X+(gx-32)/32;
	layout = SafariSv_GetLayoutPointer(SaveData_GetSafariSave(fsys->savedata),SAFARI_LAYOUT_MINE);	//編集できるのは自分のだけ

	if(layout->block[block_id].obj_ct >= SAFARI_OBJ_MAX){
		return 1;	//もういっぱい
	}
	//自機の状態からチェックするモードを取得する
	if( Player_FormGet(fsys->player) != HERO_FORM_SWIM ){
		return 0;	//地上の時はアトリビュートに話しかけた時点で1x1を必ずおける
	}
	//水場の場合、噴水をもっていないと置く物がない
	if(!safari_WaterObjCheck(fsys->savedata)){
		return 2;
	}
	//水上の場合、2x2が置けないとダメ
	if(!attr_ObjPlaceEnable2x2( fsys, gx,gz,dir,vec.y,&px,&pz,SAFOBJ_PLACE_MODE_WATER)){
		return 3;
	}
	return 0;
}


/**
 *	@brief	話しかけた位置にあるサファリOBJのデータindexを特定
 */
SAFARI_OBJ_ID FieldSafari_SearchTalkObj(FIELDSYS_WORK* fsys,u16* data_idx)
{
	int gx,gz,block_id,i,dir;
	SAFARI_LAYOUT* layout;
	SAFARI_BLOCK*	pb;
	u8 my_sex;

	//プレイヤーの話しかけ座標を取得
	dir = Player_DirGet(fsys->player);
	gx = Player_NowGPosXGet(fsys->player) + FieldOBJ_DirAddValueGX(dir);
	gz = Player_NowGPosZGet(fsys->player) + FieldOBJ_DirAddValueGZ(dir);
	my_sex = MyStatus_GetMySex(SaveData_GetMyStatus(fsys->savedata));

	block_id = SafariField_GetBlockIdFromXZ(gx,gz);

	//ローカル座標に変換
	gx %= 32;
	gz %= 32;

	layout = SafariSv_GetLayoutPointer(
				SaveData_GetSafariSave(fsys->savedata),SAFARI_LAYOUT_MINE);	//編集できるのは自分のだけ
	pb = &layout->block[block_id];

	for(i = 0;i < pb->obj_ct;i++){
		SAFARI_OBJ*	obj;
		SAFARI_OBJ_PARAM	prm;

		obj = &pb->obj[i];
		SafariData_GetObjectParam(&prm,obj->id,my_sex);

		if(	gx < obj->x ||
			gz > obj->z ||
			gx >= (obj->x+prm.siz_x) ||
			gz <= (obj->z-prm.siz_z)){
			continue;
		}
		IWASAWA_PRINT("SafariObjSearch hit = %d -> obj_id=%d\n",i,obj->id);
		if(data_idx != NULL){
			*data_idx = i;
		}
		return obj->id;
	}
	//ここにはこないハズ
	*data_idx = 0;
	return SAFARI_OBJ_ID_NULL;
}

/**
 *	@brief	空きスペースがあるかとある場合の配置座標をチェック
 */
static BOOL attr_ObjPlaceEnable1x2(FIELDSYS_WORK* fsys,
	s16 gx,s16 gz,u8 dir,fx32 gy,s16* px,s16* pz,u8 mode)
{
	MATR attr;
	BOOL hit;
	int i;
	fx32	height;
	u8	out;
	const s8 diff[] = {-1,1};

	*px = gx;
	*pz = gz;

	switch(dir){
	case DIR_UP:
	case DIR_DOWN:
		{
		u8 ct = 0,idx = 0;
		s16 tz;

		if(dir == DIR_UP){
			idx = 0;
		}else{
			idx = 1;
		}

		for(i = 0;i < 2;i++){
			tz = gz + (i+1)*diff[idx];
			attr = GetAttributeLSB(fsys, gx, tz);
			hit = GetHitAttr(fsys, gx, tz);
			IWASAWA_PRINT("SObjCheck %d,%d\n",gx,tz);
			if(!attr_IsObjPlace(attr,hit,mode)){
				continue;
			}
			height = GetGroundMapHeight( fsys, gy,
						FX32_CONST(gx*16+8), FX32_CONST(tz*16+8), &out);
			if(out != HEIGHT_NORMAL || height != gy){
				continue;
			}
			++ct;
		}
		if(ct == 2){
			*px = gx;
			if(dir == DIR_UP){
				*pz = gz-1;
			}else{
				*pz = gz+2;
			}
			return TRUE;
		}
		break;
		}
	case DIR_LEFT:
	case DIR_RIGHT:
		if(dir == DIR_LEFT){
			gx += diff[0];
		}else{
			gx += diff[1];
		}

		attr = GetAttributeLSB(fsys, gx, gz);
		hit = GetHitAttr(fsys, gx, gz);
		height = GetGroundMapHeight( fsys, gy,
					FX32_CONST(gx*16+8), FX32_CONST(gz*16+8), &out);

		IWASAWA_PRINT("SObjCheck %d,%d\n",gx,gz);
		if((!attr_IsObjPlace(attr,hit,mode)) || (out != HEIGHT_NORMAL) || (height != gy)){
			return FALSE;
		}
		//上下チェック
		for(i = 0;i < 2;i++){
			s16	tz = gz+diff[i];
			attr = GetAttributeLSB(fsys, gx, tz);
			hit = GetHitAttr(fsys, gx, tz);
			IWASAWA_PRINT("SObjCheck %d,%d\n",gx,tz);
#if 0
			height = GetGroundMapHeight( fsys, gy,
						FX32_CONST(gx*16+8), FX32_CONST(tz*16+8), &out);

			if((!attr_IsObjPlace(attr,hit,mode)) || (out != HEIGHT_NORMAL) || (height != gy)){
				continue;
			}
			*px = gx;
			*pz = gz+(-1*i);
#else
			height = GetGroundMapHeight( fsys, gy,
						FX32_CONST(gx*16+8), FX32_CONST(tz*16+8), &out);

			if((!attr_IsObjPlace(attr,hit,mode)) || (out != HEIGHT_NORMAL) || (height != gy)){
				continue;
			}
			*px = gx;
			*pz = gz+(1*i);
#endif
			return TRUE;
		}
		break;
	}
	return FALSE;
}

/**
 *	@brief	空きスペースがあるかとある場合の配置座標をチェック2x1
 *
 *	＊目の前は話しかけチェックで走査済み
 */
static BOOL attr_ObjPlaceEnable2x1(FIELDSYS_WORK* fsys,
	s16 gx,s16 gz,u8 dir,fx32 gy,s16* px,s16* pz,u8 mode)
{
	MATR attr;
	BOOL hit;
	int i;
	u8	out;
	fx32	height;
	const s8 diff[] = {-1,1};

	*px = gx;
	*pz = gz;

	switch(dir){
	case DIR_UP:
	case DIR_DOWN:
		if(dir == DIR_UP){
			gz += diff[0];
		}else{
			gz += diff[1];
		}

		attr = GetAttributeLSB(fsys, gx, gz);
		hit = GetHitAttr(fsys, gx, gz);
		height = GetGroundMapHeight( fsys, gy,
					FX32_CONST(gx*16+8), FX32_CONST(gz*16+8), &out);

		IWASAWA_PRINT("SObjCheck %d,%d\n",gx,gz);
		if((!attr_IsObjPlace(attr,hit,mode)) || (out != HEIGHT_NORMAL) || (height != gy)){
//		if((!attr_IsObjPlace(attr,hit,mode))){
			return FALSE;
		}
		for(i = 0;i < 2;i++){
			s16 tx = gx+diff[i]*-1;
			attr = GetAttributeLSB(fsys, tx, gz);
			hit = GetHitAttr(fsys, tx, gz);
			height = GetGroundMapHeight( fsys, gy,
						FX32_CONST(tx*16+8), FX32_CONST(gz*16+8), &out);

			IWASAWA_PRINT("SObjCheck %d,%d\n",tx,gz);
			if((!attr_IsObjPlace(attr,hit,mode)) || (out != HEIGHT_NORMAL) || (height != gy)){
//			if((!attr_IsObjPlace(attr,hit,mode))){
				continue;
			}
			*px = gx+(-1*i);
			*pz = gz;
			return TRUE;
		}
		break;
	case DIR_LEFT:
	case DIR_RIGHT:
		{
		u8 ct = 0,idx = 0;
		s16 tx,sx;

		if(dir == DIR_LEFT){
			sx = gx-2;
		}else{
			sx = gx+1;
		}
		for(i = 0;i < 2;i++){
			tx = sx + i;
			attr = GetAttributeLSB(fsys, tx, gz);
			hit = GetHitAttr(fsys, tx, gz);
			IWASAWA_PRINT("SObjCheck %d,%d\n",tx,gz);
			if(!attr_IsObjPlace(attr,hit,mode)){
				continue;
			}
#if 1
			height = GetGroundMapHeight( fsys, gy,
						FX32_CONST(tx*16+8), FX32_CONST(gz*16+8), &out);
			if(out != HEIGHT_NORMAL || height != gy){
				continue;
			}
#endif
			++ct;
		}
		if(ct == 2){
			*px = sx;
			*pz = gz;
			return TRUE;
		}
		break;
		}
	default:
		break;
	}
	return FALSE;
}

/**
 *	@brief	空きスペースがあるかとある場合の配置座標をチェック2x2
 */
static BOOL attr_ObjPlaceEnable2x2(FIELDSYS_WORK* fsys,
	s16 gx,s16 gz,u8 dir,fx32 gy,s16* px,s16* pz,u8 mode)
{
	MATR attr;
	BOOL hit;
	int i;
	const s8 diff[] = {1,-1};

	*px = gx;
	*pz = gz;

	switch(dir){
	case DIR_UP:
	case DIR_DOWN:
		//目の前チェック
		if(!attr_ObjPlaceEnable1x2(fsys,gx,gz,dir,gy,px,pz,mode)){
			break;
		}
		//左右チェック
		for(i = 0;i < 2;i++){
			if(attr_ObjPlaceEnable1x2(fsys,gx + diff[i],gz,dir,gy,px,pz,mode)){
				*px = gx + (i*-1);
				if(dir == DIR_UP){
					*pz = gz-1;
				}else{
					*pz = gz+2;
				}
				return TRUE;
			}
		}
		break;
	case DIR_LEFT:
	case DIR_RIGHT:
		//目の前チェック
		if(!attr_ObjPlaceEnable2x1(fsys,gx,gz,dir,gy,px,pz,mode)){
			break;
		}
		//上下チェック
		for(i = 0;i < 2;i++){
#if 0
			if(attr_ObjPlaceEnable2x1(fsys,gx + (diff[i]*-1),gz,dir,gy,px,pz,mode)){
				if(dir == DIR_LEFT){
					*px = gx-2;
				}else{
					*px = gx+1;
				}
				*pz = gz + (i*1);
				return TRUE;
			}
#else
			if(attr_ObjPlaceEnable2x1(fsys,gx, gz + (diff[i]*-1),dir,gy,px,pz,mode)){
				if(dir == DIR_LEFT){
					*px = gx-2;
				}else{
					*px = gx+1;
				}
				*pz = gz + (i*1);
				return TRUE;
			}
#endif
		}
		break;
	}
	return FALSE;
}

/**
 *	@brief	配置可能アトリビュートチェック
 */
static BOOL attr_IsObjPlace(MATR matr,BOOL hit,SAFOBJ_PLACE_MODE mode)
{
	if(mode == SAFOBJ_PLACE_MODE_GROUND){
		return SafariAttr_IsGroundObjPlace(matr,hit);
	}else{
		return SafariAttr_IsWaterObjPlace(matr,hit);
	}
}
